package org.adream.dreamnt;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationContext;
import org.jasig.cas.authentication.AuthenticationContextBuilder;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.AuthenticationTransaction;
import org.jasig.cas.authentication.DefaultAuthenticationContextBuilder;
import org.jasig.cas.authentication.DefaultAuthenticationSystemSupport;
import org.jasig.cas.authentication.UsernamePasswordCredential;
import org.jasig.cas.ticket.AbstractTicketException;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.jasig.cas.web.support.WebUtils;
import org.jose4j.json.JsonUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.webflow.execution.RequestContextHolder;

@Component("customLogiController")
@Controller
public class CasCustomLoginRestfulApi extends AbstractController {

	private CentralAuthenticationService centralAuthenticationService;
	private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;
	private DefaultAuthenticationSystemSupport defaultAuthenticationSystemSupport;
	
	@Value("$server.name}")
	private String serverName;

	/**
	 * 
	 * 
	 * 功能：获取用户名密码,验证有效性,生成相关票据并绑定注册,添加cookie
	 * 
	 * @author [url=mailto:engineer03@financegt.com]zqb[/url]
	 * @date 2016年7月5日
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	@RequestMapping(path = "/loginApi")
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		String client = request.getParameter("client_id");
		if(client!=null){
			return loginByOthers(request,response,client);
		}
		String username = request.getParameter("un");
		String password = request.getParameter("pd");
		
		if(StringUtils.isEmpty(username)){
			username = request.getParameter("username");
		}
		if(StringUtils.isEmpty(password)){
			password = request.getParameter("password");
		}
		return doLogin(username,password,request,response);
	}
	
	private ModelAndView doLogin(String username,String password,HttpServletRequest request, HttpServletResponse response) throws InstantiationException, IllegalAccessException, UnsupportedEncodingException {
		ModelAndView signinView = new ModelAndView();


		// try {
		// username = new String(new BASE64Decoder().decodeBuffer(username)); //
		// 解密后
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
		// try {
		// password = new String(new BASE64Decoder().decodeBuffer(password));
		// } catch (IOException e) {
		// e.printStackTrace();
		// }

		logger.debug("解密后的账号:" + username);
		// System.out.println("解密后的密码:" + password);
		// username = EncryptUrlPara.decrypt("username",username);
		// password = EncryptUrlPara.decrypt("password",password);
		logger.debug("开始绑定ticket");
		String rlt = null;
		
		try {
			if(username==null){
				rlt="系统错误";
				logger.error("username=null");
			}else{
				bindTicketGrantingTicket(username, password, request, response);
			}
		} catch (AuthenticationException e) {
			logger.debug(e.getMessage(), e);
			Collection<Class<? extends Exception>> c = e.getHandlerErrors().values();
			if(!c.isEmpty()){
				String x = c.iterator().next().getSimpleName();
				switch(x){
				case "FailedLoginException":
					rlt = "帐号或者密码错误。";
					break;
				case "CheckcodeWrongException":
					rlt = "验证码错误。";
					break;
				case "BlackListUserException":
					rlt = "您的账号已禁止登录，请联系管理员。";
					break;
				case "UserLoginLockException":
					rlt = "您的账号已被锁定一小时，请稍后再试。";
					break;
				case "UserLoginExceptionException":
					rlt = "您的账号存在登录异常，请联系管理员。";
					break;
				default:
					e.getHandlerErrors().forEach((str,val)->{
						logger.warn("错误信息："+str);
						logger.warn("异常信息："+val);
					});
					logger.error(e.getMessage(),e);
					rlt = "系统错误，请联系管理员.";
				}
			}
		} catch (Exception e) {
			logger.warn(e.getMessage(), e);
			rlt = "系统错误，请联系管理员。";
		}
		if (rlt == null) {// 登录成功
			logger.debug("登录成功。");
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login" + (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			signinView.setViewName(url);
		} else {
			logger.debug("登录失败。");
//			String fail = ServletRequestUtils.getStringParameter(request, "fail", "");
//			String url = "redirect:" + fail;
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login" + (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ? "&msg=" : "?msg=";
			try {
				url += URLEncoder.encode(rlt, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				url += rlt;

			}
			signinView.setViewName(url);
		}
		return signinView;
	}
	//微信应用访问
	private final String WXAPP_APPID = "wx8e07f3dae08c1071";
	private final String WXAPP_SECRET = "940fa2320f14aeba28882619eaca5f4e";
	//微信二维码
	private final String WXUSER_APPID = "wxc1bf3b1631aba697";
	private final String WXUSER_SECRET = "e643ec1ce59101090bdd1116927d1a6f";
    //qq
	private final String QQ_APPID = "101862516";
    private final String QQ_APPKEY = "339bb58edee4e23b7d9a2b38989728c9";

	protected ModelAndView loginByOthers(HttpServletRequest request, HttpServletResponse response, String client) throws Exception{
		switch (client){
		case "wxapp"://微信游览器直接登录
			return loginByWeixin(request,response,true);
		case "wxuser"://扫描二维码登录
			return loginByWeixin(request,response,false);
		case "qq"://qq登录
			return loginByQQ(request, response);
		}
		return doLogin(null,null,request,response);
	}
	
	
	protected ModelAndView loginByWeixin(HttpServletRequest request, HttpServletResponse response, boolean isWeixinApp) throws Exception{
		String code = request.getParameter("code");
		String state = request.getParameter("state");
		logger.debug("loginByOthers");
		logger.debug("CODE:"+code);
		logger.debug("STATE:"+state);
		logger.debug("isWeixinApp:"+isWeixinApp);
		
		String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
		// 2.获取授权码
		String aTokenJsonString = WeixinUtil.getAccess_token(isWeixinApp?String.format(tokenUrl, WXAPP_APPID,WXAPP_SECRET,code):String.format(tokenUrl, WXUSER_APPID,WXUSER_SECRET,code));

		logger.debug("这里是aTokenJsonString" + aTokenJsonString);
		Map<String,Object> map = JsonUtil.parseJson(aTokenJsonString);
		String unionid = String.valueOf(map.get("unionid"));
		logger.debug("unionid:"+unionid);
		
		if(StringUtils.isEmpty(unionid)){
			ModelAndView signinView = new ModelAndView();
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login";
			url += (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ?"&client_id=errwxuser":"?client_id=errwxuser";
			url += "&msg=" + URLEncoder.encode("微信登录失败，您可以尝试更换游览器.", "UTF-8");
			signinView.setViewName(url);
			return signinView;
		}
		
		//将unionid,nickName 放入session 下次登录将自动进行绑定
		HttpSession session = request.getSession();
		session.setAttribute("WX_UNIONID", unionid);
		
		//查找相关联的数据
		DataSource dataSource = getWebApplicationContext().getBean("dataSource",DataSource.class );
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		// 1. 用户是否已经绑定微信
		String uid = null;
		try{
			uid = jdbcTemplate.queryForObject("select uid from acc_user where unionid = ?", String.class,unionid);
		}catch(EmptyResultDataAccessException e){
			//木有用户 抛出异常 返回登录页面
			logger.debug("微信验证失败。");
			
			/**
			 * 获取nickname
			 */
			String accessToken = String.valueOf(map.get("access_token"));
			String openid = String.valueOf(map.get("openid"));
			logger.debug("accessToken:" + accessToken);
			logger.debug("openid:" + openid);
			
			// 3.获取用户信息
			String getInfoUrl =  "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openid;
			String userInfo = WeixinUtil.getAccess_token(getInfoUrl);
			//解决微信中文昵称乱码
			Map<String, Object> userInfoJsonString = JsonUtil.parseJson(userInfo);
			String nickName = String.valueOf(userInfoJsonString.get("nickname"));
			//获取微信头像
			String headimgurl = String.valueOf(userInfoJsonString.get("headimgurl"));
			
			ModelAndView signinView = new ModelAndView();
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login";
			url += (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ?"&client_id=wxuser":"?client_id=wxuser";
			if(!isWeixinApp){
				url += "&msg=" + URLEncoder.encode("该微信用户尚未绑定账号.", "UTF-8");
			}
			signinView.setViewName(url);
//			//将unionid,nickName 放入session 下次登录将自动进行绑定
//			HttpSession session = request.getSession();
//			session.setAttribute("WX_UNIONID", unionid);
			session.setAttribute("WX_NICKNAME", nickName);
			//将headimgurl放入session
			session.setAttribute("WX_HEADIMGURL", headimgurl);
			return signinView;
		}

		String randomStr = UUID.randomUUID().toString();
		//插入数据库
    	jdbcTemplate.update("REPLACE into acc_oauth_memory (uid,login,token,expire) values (?,?,?,DATE_ADD(now(),INTERVAL 1 MINUTE))", uid,unionid,randomStr);
		
		return doLogin(unionid,randomStr,request,response);
	}
	
	protected ModelAndView loginByQQ(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String code = request.getParameter("code");
		String state = request.getParameter("state");
		logger.debug("loginByQQ");
		logger.debug("CODE:"+code);
		logger.debug("STATE:"+state);
		//通过Authorization Code获取Access Token
        String getAccessTokenUrl = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%&client_secret=%&code=%&redirect_uri=%";
        logger.debug("get access token url:" + getAccessTokenUrl);
        /**
         * 如果成功返回,即可在返回包中获取access token 
         * 		如:access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
         * 错误时,会返回code和msg字段,以url参数对的形式返回,value部分会进行url编码（UTF-8）
         */
        String getAccessTokenResult = WeixinUtil.httpGet(String.format(getAccessTokenUrl, QQ_APPID, QQ_APPKEY, URLEncoder.encode(serverName + "/cas/loginApi?client_id=qq", "UTF-8")));
        Map<String,String> accessTokenMap = Util.urlParamToMap(getAccessTokenResult);
        if(accessTokenMap.containsKey("code") || StringUtils.isEmpty(accessTokenMap.get("access_token"))) {
        	logger.warn("获取access token失败,:" + accessTokenMap.get("msg"));
        	ModelAndView signinView = new ModelAndView();
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login";
			url += (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ?"&client_id=errqquser":"?client_id=errqquser";
			url += "&msg=" + URLEncoder.encode("qq登录失败，您可以尝试更换登录方式.", "UTF-8");
			signinView.setViewName(url);
			return signinView;
        }
        String accessToken = accessTokenMap.get("access_token");
        String getOpenIdUrl = "https://graph.qq.com/oauth2.0/me?access_token=%";
        /**
         * 成功:callback({"client_id":"YOUR_APPID","openid":"YOUR_OPENID"});
         * 失败:返回code和msg字段,以url参数对的形式返回,value部分会进行url编码(UTF-8)
         */
        String getOpenIdResult = WeixinUtil.httpGet(String.format(getOpenIdUrl, accessToken));
        if(getOpenIdResult.indexOf("code") != -1) {
        	logger.warn("获取openid失败,:" + Util.urlParamToMap(getOpenIdResult).get("msg"));
        	ModelAndView signinView = new ModelAndView();
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login";
			url += (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ?"&client_id=errqquser":"?client_id=errqquser";
			url += "&msg=" + URLEncoder.encode("qq登录失败，您可以尝试更换登录方式.", "UTF-8");
			signinView.setViewName(url);
			return signinView;
        }
        Map<String,Object> openIdMap = JsonUtil.parseJson(getOpenIdResult.substring(getOpenIdResult.indexOf("(") + 1, getOpenIdResult.indexOf(")")));
        String openId = (String)openIdMap.get("openid");
        
		// 查找相关联的数据
		DataSource dataSource = getWebApplicationContext().getBean("dataSource", DataSource.class);
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		// 1. 用户是否已经绑定qq
		String uid = null;
		try {
			uid = jdbcTemplate.queryForObject("select uid from acc_user where qqUnionid = ?", String.class, openId);
		} catch (EmptyResultDataAccessException e) {
			// 木有用户 抛出异常 返回登录页面
			logger.debug("qq验证失败。");
			ModelAndView signinView = new ModelAndView();
			String service = ServletRequestUtils.getStringParameter(request, "service", "");
			String url = "redirect:login";
			url += (service.length() > 0 ? "?service=" + URLEncoder.encode(service, "UTF-8") : "");
			url += (url.indexOf("?") > -1) ? "&client_id=qquser" : "?client_id=qquser";
			signinView.setViewName(url);
			// 将openid放入session 下次登录将自动进行绑定
			HttpSession session = request.getSession();
			session.setAttribute("QQ_OPENID", openId);
			return signinView;
		}

		String randomStr = UUID.randomUUID().toString();
		// 插入数据库
		jdbcTemplate.update(
				"REPLACE into acc_oauth_memory (uid,login,token,expire) values (?,?,?,DATE_ADD(now(),INTERVAL 1 MINUTE))",
				uid, openId, randomStr);
		return doLogin(openId, randomStr, request, response);
	}

	/**
	 * Invoke generate validate Tickets and add the TGT to cookie.
	 * 
	 * @param loginName
	 *            the user login name.
	 * @param loginPassword
	 *            the user login password.
	 * @param request
	 *            the HttpServletRequest object.
	 * @param response
	 *            the HttpServletResponse object.
	 */
	/**
	 * 
	 * 
	 * 功能：具体生成相关票据并绑定注册,添加cookie实现方法
	 * 
	 * @author [url=mailto:engineer03@financegt.com]zqb[/url]
	 * @date 2016年7月5日
	 * @param loginName
	 * @param loginPassword
	 * @param request
	 * @param response
	 * @throws AuthenticationException 
	 * @throws AbstractTicketException 
	 */
	protected void bindTicketGrantingTicket(String loginName, String loginPassword, HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException, AbstractTicketException {
		UsernamePasswordCredential credentials = new UsernamePasswordCredential();
		credentials.setUsername(loginName);
		credentials.setPassword(loginPassword);

		final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder(
				defaultAuthenticationSystemSupport.getPrincipalElectionStrategy());
		final AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credentials);

		defaultAuthenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder);

		final AuthenticationContext ctx = builder.build(WebUtils.getService(RequestContextHolder.getRequestContext()));
		String ticketGrantingTicket = centralAuthenticationService.createTicketGrantingTicket(ctx).getId();

		ticketGrantingTicketCookieGenerator.addCookie(request, response, ticketGrantingTicket);
	}

	public CentralAuthenticationService getCentralAuthenticationService() {
		return centralAuthenticationService;
	}

	public void setCentralAuthenticationService(CentralAuthenticationService centralAuthenticationService) {
		this.centralAuthenticationService = centralAuthenticationService;
	}

	public CookieRetrievingCookieGenerator getTicketGrantingTicketCookieGenerator() {
		return ticketGrantingTicketCookieGenerator;
	}

	public void setTicketGrantingTicketCookieGenerator(
			CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
		this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator;
	}

	public DefaultAuthenticationSystemSupport getDefaultAuthenticationSystemSupport() {
		return defaultAuthenticationSystemSupport;
	}

	public void setDefaultAuthenticationSystemSupport(
			DefaultAuthenticationSystemSupport defaultAuthenticationSystemSupport) {
		this.defaultAuthenticationSystemSupport = defaultAuthenticationSystemSupport;
	}
}
